use strict;
use warnings;
use POE qw(Component::IRC 
	   Component::IRC::Plugin::CPAN::LinksToDocs
	   Component::IRC::Plugin::URI::Find
	   Component::IRC::Plugin::Google::Calculator
           Component::IKC::Server
           Component::IKC::Specifier
	   );
use Data::Dumper;
use POE::Component::IKC::ClientLite;
use DateTime;
use constant PATH => $ENV{HOME} ."/irc/";
use Encode qw/encode decode/;
use URI;
use URI::Escape;
use YAML::Syck;
use Web::Scraper;
use LWP::UserAgent;
use Net::Twitter;
use Me2day;

my $nickname = 'Perl_^^';
my $ircname = 'Perl Bot :-)';
my $server = 'irc.hanirc.org';

my $translator = 'http://j2k.naver.com/j2k.php/korean/';
my $dic_url = 'http://endic.naver.com/search.nhn?kind=keyword&query=';
my $naver_map_url = 'http://map.naver.com/?query=';

my $lang = "ko";
my $google_url = 'http://www.google.com/search?hl='.$lang.'&q=';

my $ping_time = 0;

my $info;

my @channels = ('#perl');

my $base_url = "";

binmode(STDOUT, ":utf8");

my $config;
if (-f 'config.yaml') {
    $config = LoadFile('config.yaml');
}

my $name = "Client$$";

my $remote;

my $twit = Net::Twitter->new( 
    username => $config->{twitter}->{username}, 
    password => $config->{twitter}->{password},
);

my $me2day = Me2day->new(
    username => $config->{me2day}->{username},
    user_key => $config->{me2day}->{user_key},
    app_key  => $config->{me2day}->{app_key},
);

POE::Component::IKC::Server->spawn(
    port => 31337,
    name => 'AppServer',
);

POE::Session->create(
    inline_states => {
        _start        => \&service_start,
        update      => \&service_update,
        did_something => \&service_response,
      }
);


unless (-d PATH) {
    mkdir PATH;
    print STDERR "Created Directory ".PATH."\n";
}


my $irc = POE::Component::IRC->spawn( 
				      nick => $nickname,
				      ircname => $ircname,
				      server => $server,
				      ) or die "Oh noooo! $!";


POE::Session->create(
    package_states => [
        main => [ qw(_default _start irc_001 irc_public irc_msg irc_urifind_uri irc_join irc_part irc_quit irc_353) ],
    ],
    heap => { irc => $irc },
);

POE::Kernel->run();

sub service_start {
    my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];

    my $service_name = "apphanirc";
    $kernel->alias_set($service_name);
    $kernel->call( IKC => publish => $service_name, ["update"] );
}

sub service_update {
    my ( $kernel, $heap, $request ) = @_[ KERNEL, HEAP, ARG0 ];

    $kernel->delay_set( did_something => 1, $request );
}

sub service_response {
    my ( $kernel, $heap, $res ) = @_[ KERNEL, HEAP, ARG0 ];
    my @data = @{ $res };
    $irc->yield( privmsg => "#perl" => $data[0] );
}

sub _encode {
    encode("cp949", decode("utf-8", shift));
}

sub _start {
    my $heap = $_[HEAP];
    
    my $irc = $heap->{irc};

    $irc->yield( register => 'all' );
    $irc->plugin_add(	'LinksToDocs' => POE::Component::IRC::Plugin::CPAN::LinksToDocs->new );
    $irc->plugin_add(   'UriFind' =>     POE::Component::IRC::Plugin::URI::Find->new         );
    $irc->plugin_add(   'GoogleCalc' =>  POE::Component::IRC::Plugin::Google::Calculator->new );
    $irc->yield( connect => { } );
    return;
}

sub irc_001 {
    my $sender = $_[SENDER];
    
    my $irc = $sender->get_heap();
    
    print "Connected to ", $irc->server_name(), "\n";

    $irc->yield( join => $_ ) for @channels;
    return;
}

sub irc_msg {
    my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
    my $nick = ( split /!/, $who)[0];
    my $me = $where->[0];

    my $irc = $sender->get_heap();

    my ($channel,$target);
    my @block;
    if ($what =~ /^!([a-z]+)\s?(.*?)?$/) {
	my ($command, $desc) = ($1,$2);
	if ($command eq 'blah') {
	    @block = split /\s+/, $desc;
	    $channel = shift @block;
	    my $blah = join(" ", @block);
	    if ($channel && $channel =~ /^\#.*/ && $blah) {
		$irc->yield( privmsg => $channel => $blah );
	    }
	}
	elsif ($command eq 'kick') {
	    ($channel, $target) = split /\s+/, $desc;
            unless ($target) {
                $irc->yield( privmsg => $nick => _encode($config->{command}->{kick}) );
            } else {
                $irc->yield( kick => $channel => $target );
            }
	}
	elsif ($command eq 'oper') {
	    ($channel, $target) = split /\s+/, $desc;
            $irc->yield( mode => $channel => "+oo" => $target );
	}
        elsif ($command eq 'reload') {  # Config Reloader
            if (-f "config.yaml") {
                $config = LoadFile("config.yaml");
                $irc->yield( privmsg => $nick => "reloaded config file" );
            }
        }
    }
}

sub irc_public {
    my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
    my $nick = ( split /!/, $who )[0];
    my $channel = $where->[0];

    $channel =~ s/#//;
    unless ($remote) {
        $remote = create_ikc_client(
            port => 31338,
            name => $name,
            timeout => 1,
        );
    }

    $remote->post_respond( 'application/update', encode("utf-8", decode("cp949","[hanIRC] $nick: ".$what)) );

    unless(-d PATH.$channel) {
	print STDERR "Created Directory ". PATH.$channel . " for IRC Channel ".$channel."\n";
	mkdir PATH.$channel;
    }

    # Filtering by aero
    my $str = decode("euc-kr", $what);
    return 0 if $str =~ m/^\x{3151}/;

    my $time = DateTime->now( time_zone => 'Asia/Tokyo' );

    my $filepath = PATH.$channel."/".$time->ymd.".log";
    open my $fh, ">>:utf8", $filepath;
    my $desc = "[".$time->hms."] <".decode("cp949", $nick)."> ". 
	decode("cp949", $what)."\n";
    print $fh $desc;
    print $desc;
    close $fh;   

    my $irc = $sender->get_heap();

    # $command, $desc
    if ($what =~ /^!([a-z0-9]+)\s?(.*?)?$/) {
	my ($command, $desc) = ($1, $2);
        if ($command =~ /^(?:fish|nolog|kick|code|dic|search|map|twitter|me2day)$/ && !$desc) {
            $irc->yield( privmsg => "#".$channel => $nick." : "._encode($config->{command}->{$command}) );
        }
	elsif ($command eq 'kick') {
                $irc->yield( kick => "#".$channel => $desc );
	}
        elsif ($command eq 'j2k') {
            my $address = $translator;
            if ($desc) { 
                $address .= $desc;
            }
            elsif ($base_url) {
                $address .= $base_url;
            }
            else { 
                return;
            }
            $irc->yield( privmsg => "#".$channel => $address );
        }
        elsif ($command eq 'help') {
            foreach my $comment (@{ $config->{command}->{help} }) {
                $irc->yield( privmsg => "#".$channel => _encode($comment) );
            }
        }
        elsif ($command eq 'dic') {
            my $html = scraper {
                process 'table>tr>td[class="p3"]', text => 'TEXT';
            };
            
            my $data = $html->scrape(URI->new($dic_url.URI::Escape::uri_escape($desc)));
            
            my $text = $data->{text};
            
            $text = encode("cp949", $text) if utf8::valid($text);
            
            my @voca = split /\d/, $text;
            
            for my $dic (@voca) {
                $irc->yield( privmsg => "#".$channel => $dic );
            }
        }
	elsif($command eq 'map')
	{
		my $address = $naver_map_url . URI::Escape::uri_escape($desc);
		$irc->yield(privmsg=>"#".$channel=>$address);
	}
        elsif ($command eq 'search') {
            my $html = scraper {
                process 'div[class="g"]', text => 'TEXT';
            };
            
            my $data = $html->scrape(URI->new($google_url.URI::Escape::uri_escape($desc)));
            
            my $text = $data->{text};
            $text = encode("cp949", $text) if utf8::valid($text);
            
            my @entry = split /\d/, $text;
                
            for my $content (@entry) {
                $irc->yield( notic => "#".$channel => $content );
            }
        }
        elsif ($command eq 'lang') {
            if (!$desc || $desc !~ /^(?:en|ko|ja)$/) {
                $irc->yield( privmsg => "#".$channel => $nick." : "._encode($config->{command}->{lang}) );
            } else {
                $lang = $desc;
                $irc->yield( privmsg => "#".$channel => $nick." : "._encode($config->{log}->{lang}). ' => '.$lang );
            }
        }
        elsif ($command eq 'twitter') {
            $twit->update(encode("utf-8", decode("cp949", $nick. ": ".$desc)));
            $irc->yield( privmsg => "#".$channel => $nick ." : ". _encode($config->{log}->{twitter}) );
        }
        elsif ($command eq 'me2day') {
            my $res;
            if ($desc =~ /@\:/) {
               my ($post, $tag) = split /@\:/, $desc; #/
               $tag .= " me2irc";
               $res = $me2day->create_post( body => encode("utf-8", decode("cp949",$nick." : ".$post)), tags => encode("utf-8", decode("cp949", $tag)) );
           } else {
               $res = $me2day->create_post( body => encode("utf-8", decode("cp949",$nick." : ".$desc)), tags => "me2irc" );
           }
            if ($res->status_line =~ /200/) {
                $res->content =~ /<permalink>(.*?)<\/permalink>/;
                my $permalink = $1;
                $irc->yield( privmsg => "#".$channel => $nick ." : ". _encode($config->{log}->{me2day}). " ".$permalink );
            }
        }
        elsif ($command eq 'attention') {
            if ($info->{"#".$channel}) {
                $irc->yield( privmsg => "#".$channel => join("  ", @{ $info->{"#".$channel} }) );
            }
        }
    }
}

sub irc_join {
    my ($sender, $who, $where) = @_[SENDER, ARG0, ARG1];
    my $nick = ( split /!/, $who )[0];
    
    unless ($remote) {
        $remote = create_ikc_client(
            port => 31338,
            name => $name,
            timeout => 1,
        );
    }
    $remote->post_respond( 'application/update', encode("utf-8", decode("cp949","[hanIRC] $nick joined $where")) );
}

sub irc_part {
    my ($sender, $who, $where, $msg) = @_[SENDER, ARG0 .. ARG2];
    my $nick = (split /!/, $who)[0];
    
    unless ($remote) {
        $remote = create_ikc_client(
            port => 31338,
            name => $name,
            timeout => 1,
        );
    }
    $remote->post_respond( 'application/update', encode("utf-8", decode("cp949","[hanIRC] $nick parted $where")) ); 
}

sub irc_quit {
    my ($sender, $who, $msg) = @_[SENDER, ARG0, ARG1];
    my $nick = (split /!/, $who)[0];

    unless ($remote) {
        $remote = create_ikc_client(
            port => 31338,
            name => $name,
            timeout => 1,
        );
    }

    $remote->post_respond( 'application/update', encode("utf-8", decode("cp949","[hanIRC] $nick is quit")) ); 
}

sub irc_353 {
    my ($sender, $server, $desc, $desc_a) = @_[SENDER, ARG0 .. ARG2];

    my ($temp, $channel, $members) = @{ $desc_a };

    @{ $info->{$channel} }= map { s/@//; $_ } split /\s/, $members;
}

sub _default {
    my ($event, $args) = @_[ARG0 .. $#_];
    my @output = ( "$event: " );

#    irc_rss_notify($event);

    for my $arg (@$args) {
	if ( ref $arg eq 'ARRAY' ) {
	    push( @output, '[' . join(' ,', @$arg ) . ']' );
	}
	else {
	    push ( @output, "'$arg'" );
	}
    }
    print join ' ', @output, "\n";
    return 0;
}

# IRC RSS Notify
sub irc_rss_notify {
    my ($event) = shift;

    if ($event eq 'irc_ping') {
        $ping_time++;

        if ($ping_time % 5 == 0) {
            system("perl aggregate.pl");
        }

        if ($ping_time % 7 == 0) {
            my $data = LoadFile("result.yaml");
            foreach my $key (keys %{ $data }) {
                foreach my $feed (@{ $data->{$key} }) {
                    $irc->yield( privmsg => '#perl' => "[ ". $feed->{type} . " ] " . 
                                     _encode($feed->{title}). '<'.($feed->{author} || "Unknown").'> :: '. $feed->{link} );
                }
            }
        }
    }
}

# URL Find : Title Notify
sub irc_urifind_uri {
    my ($who, $channel, $url, $obj, $msg) = @_[ARG0 .. ARG4];
    
    my $ua = LWP::UserAgent->new;
    my $res = $ua->get($url);
    if ($res->is_success) {
        $base_url = $url;
	my $html = scraper {
	    process 'title', title => 'TEXT';
	};
	my $data = $html->scrape(URI->new($url));
	return 0 unless $data->{title};
	my $title = $data->{title};
	$title = encode('cp949', $title) if utf8::valid($title);
	$irc->yield( privmsg => $channel => $title );
        $channel =~ s/#//;
    }
}
